﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MemcachedDemo.Library.Config;
using System.Collections;

namespace MemcachedDemo.Library.Client
{
    public sealed class MemcachedManager
    {
        #region Private Property
        private static object lockObj = new object();
        private static MemcachedClient mClient = null;
        //服务器配置信息
        private static MemcachedSettings mSettings = MemcachedConfigManager.Settings;
        private static SockIOPool sockPool = null;
        #endregion

        public static MemcachedClient Instance
        {
            #region
            get
            {
                if (mClient == null)
                {
                    lock (lockObj)
                    {
                        if (mClient == null)
                        {
                            Init();
                        }
                    }
                }
                return mClient;
            }
            #endregion
        }

        private static void Init()
        {
            #region

            //设置SokectPool信息
            sockPool = GetSockIOPool(mSettings.PoolName, MemcachedConfigManager.GetServers(mSettings.ServerList));
            sockPool.SetWeights(MemcachedConfigManager.GetWeights(mSettings.ServerList));

            //实例化 Memcached客户端访问对象
            mClient = new MemcachedClient();
            mClient.PoolName = mSettings.PoolName;
            mClient.EnableCompression = false;
            #endregion
        }

        private static SockIOPool GetSockIOPool(string poolName, string[] serverList)
        {
            #region
            SockIOPool pool = SockIOPool.GetInstance(poolName);
            pool.SetServers(serverList);
            pool.InitConnections = mSettings.InitConnections;//初始化链接数
            pool.MinConnections = mSettings.MinConnections;//最少链接数
            pool.MaxConnections = mSettings.MaxConnections;//最大连接数
            pool.SocketConnectTimeout = mSettings.SocketConnectTimeout;//Socket链接超时时间
            pool.SocketTimeout = mSettings.SocketTimeout;// Socket超时时间
            pool.MaintenanceSleep = mSettings.MaintenanceSleep;//维护线程休息时间
            pool.Failover = mSettings.Failover; //失效转移(一种备份操作模式)
            pool.Nagle = mSettings.Nagle;//是否用nagle算法启动socket
            pool.HashingAlgorithm = HashingAlgorithm.NewCompatibleHash;
            pool.Initialize();
            return pool;
            #endregion
        }

        public static MemcachedClient GetClient(string poolName, string[] serverList)
        {
            #region
            mSettings = MemcachedConfigManager.Settings;
            GetSockIOPool(poolName, serverList);
            MemcachedClient mc = new MemcachedClient();
            mc.PoolName = poolName;
            mc.EnableCompression = false;
            return mc;
            #endregion
        }

        public static void Reset()
        {
            mClient = null;
        }

        public static void Dispose()
        {
            if (!mSettings.Disable && sockPool != null)
                sockPool.Shutdown();
        }

        /// <summary>
        /// 获取有效的memcached服务器地址(用于检查所有memcached服务器是否正常工作)
        /// </summary>
        public static string[] GetConnectedSocketHost()
        {
            #region
            SockIO sock = null;
            string connectedHost = null;
            foreach (ServerInfo item in mSettings.ServerList)
            {
                if (string.IsNullOrEmpty(item.Ip) == false)
                {
                    try
                    {
                        //创建链接到某memcached的socket对象
                        sock = SockIOPool.GetInstance(mSettings.PoolName).GetConnection(item.Ip + ":" + item.Port);
                        if (sock != null)
                        {
                            //如创建成功则意味着该memcached可用
                            connectedHost = string.Concat(item.Ip, ",", connectedHost);
                        }
                    }
                    finally
                    {
                        if (sock != null)
                            sock.Close();
                    }
                }
            }
            return connectedHost.Split(',');
            #endregion
        }

        /// <summary>
        /// 根据指定命令获取Memcached服务器信息
        /// </summary>
        ///<param name="paramValue">Is CachedDump value format=[slab_id limit_num]; Is Detail value format=[on|off|dump] </param>
        /// <returns></returns>
        public static StringBuilder GetStatsBy(ArrayList serverList, MemcachedStatsCommand statsComm, string paramValue)
        {
            #region
            //http://www.cnblogs.com/eoiioe/archive/2009/06/03/1495338.html
            //http://blogold.chinaunix.net/u1/45336/showart_2227790.html
            //https://github.com/memcached/memcached/blob/master/doc/protocol.txt

            StringBuilder result = new StringBuilder();
            var statsResult = GetStatsInfo(serverList, statsComm, paramValue);
            foreach (var key in statsResult.Keys)
            {
                Hashtable values = (Hashtable)statsResult[key];
                result.Append("\r\n------Server=" + key + "-------\r\n");
                foreach (var subKey in values.Keys)
                {
                    result.Append("key=[" + subKey);
                    result.Append("]value=[" + values[subKey].ToString());
                    result.Append("]\r\n");
                }
            }
            return result;
            #endregion
        }

        /// <summary>
        /// 获取Memcached服务器端的全部缓存Key;
        /// </summary>
        /// <returns>key:server value:cacheKeyList</returns>
        public static Dictionary<string, List<string>> GetAllCacheKeys()
        {
            return GetAllCacheKeys(null);
        }

        /// <summary>
        /// 获取Memcached服务器端的全部缓存Key
        /// </summary>
        /// <returns>key:server value:cacheKeyList</returns>
        public static Dictionary<string, List<string>> GetAllCacheKeys(ArrayList serverList)
        {
            #region
            Dictionary<string, List<string>> result = new Dictionary<string, List<string>>();

            var statsResult = GetStatsInfo(serverList, MemcachedStatsCommand.Slabs, null);
            Dictionary<string, List<string>> serverSlabIds = new Dictionary<string, List<string>>();
            foreach (string key in statsResult.Keys)
            {
                #region
                var values = (Hashtable)statsResult[key];
                if (!serverSlabIds.ContainsKey(key))
                {
                    serverSlabIds.Add(key, new List<string>());
                }

                foreach (var subKey in values.Keys)
                {
                    var arr = subKey.ToString().Split(':');
                    if (arr.Length > 1)
                    {
                        if (!serverSlabIds[key].Contains(arr[0]))
                        {
                            serverSlabIds[key].Add(arr[0]);
                        }
                    }
                }
                #endregion
            }

            foreach (var server in serverSlabIds)
            {
                #region
                result.Add(server.ToString(), new List<string>());
                foreach (var id in server.Value)
                {
                    var keyList = GetStatsInfo(new ArrayList(new string[] { server.Key }), MemcachedStatsCommand.CachedDump, id + " 0");
                    foreach (Hashtable item in keyList.Values)
                    {
                        foreach (var key in item.Keys)
                        {
                            result[server.ToString()].Add(key.ToString());
                        }
                    }
                }
                #endregion
            }

            return result;
            #endregion
        }

        private static Hashtable GetStatsInfo(ArrayList serverList, MemcachedStatsCommand statsComm, string paramValue)
        {
            #region
            paramValue = string.IsNullOrEmpty(paramValue) ? "" : paramValue.Trim().ToLower();
            var commandString = "stats";
            switch (statsComm)
            {
                case MemcachedStatsCommand.Reset: commandString = "stats reset"; break;
                case MemcachedStatsCommand.Malloc: commandString = "stats malloc"; break;
                case MemcachedStatsCommand.Sizes: commandString = "stats sizes"; break;
                case MemcachedStatsCommand.Slabs: commandString = "stats slabs"; break;
                case MemcachedStatsCommand.Items: commandString = "stats items"; break;
                case MemcachedStatsCommand.CachedDump: commandString = "stats cachedump " + paramValue; break;
                case MemcachedStatsCommand.Detail: commandString = "stats detail " + paramValue; break;
                default: break;
            }

            var statsResult = MemcachedManager.Instance.Stats(serverList, commandString, null);
            return statsResult;
            #endregion
        }
    }

    /// <summary>
    /// Memcached Stats 命令枚举
    /// </summary>
    public enum MemcachedStatsCommand
    {
        /// <summary>
        /// stats : 显示服务器信息、统计数据等
        /// </summary>
        Default = 0,

        /// <summary>
        /// stats reset:清空统计数据
        /// </summary>
        Reset = 1,

        /// <summary>
        /// stats malloc:内存分配
        /// </summary>
        Malloc = 2,

        /// <summary>
        /// stats sizes:
        /// </summary>
        Sizes = 3,

        /// <summary>
        /// stats slabs:显示各个slab的信息，包括chunk的大小、数目、使用情况等
        /// </summary>
        Slabs = 4,

        /// <summary>
        /// stats items:显示各个slab中item的数目和最老item的年龄(最后一次访问距离现在的秒数)
        /// </summary>
        Items = 5,

        /// <summary>
        /// stats cachedump slab_id limit_num :
        /// 1.2.4以上版本，包括1.2.4显示 过期时间(timestamp)
        /// 如果是永不过期的key，expire_time会显示为服务器启动的时间
        /// </summary>
        CachedDump = 6,

        /// <summary>
        /// stats detail [on|off|dump]:
        /// 参数为on，打开详细操作记录
        /// 参数为off，关闭详细操作记录
        /// 参数为dump，显示详细操作记录(每一个键值get、set、hit、del的次数)
        /// </summary>
        Detail = 7

    }
}
